package com.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.common.ErrorCode;
import com.example.exception.BusinessException;
import com.example.model.dto.TeamJoinRequestDTO;
import com.example.model.dto.TeamQuery;
import com.example.model.dto.TeamQuitRequestDTO;
import com.example.model.dto.TeamUpdateRequestDTO;
import com.example.model.enums.TeamStatusEnum;
import com.example.model.po.Team;
import com.example.mapper.TeamMapper;
import com.example.model.po.User;
import com.example.model.po.UserTeam;
import com.example.model.vo.TeamUserVO;
import com.example.model.vo.UserVO;
import com.example.service.ITeamService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.service.IUserService;
import com.example.service.IUserTeamService;
import io.netty.util.internal.StringUtil;
import javafx.print.PaperSource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 队伍 服务实现类
 * </p>
 *
 * @author LinHui
 * @since 2024-04-19
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements ITeamService {

    @Resource
    private IUserTeamService userTeamService;

    @Resource
    private TeamMapper teamMapper;

    @Resource
    private IUserService userService;

    @Resource
    private RedissonClient redissonClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public long addTeam(Team team, User logingUser) {
        if (team == null || logingUser == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //校验信息
        Integer maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxNum <= 1 || maxNum > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍拥有人数超出了最大限制");
        }

        String name = team.getName();
        if (StringUtils.isBlank(name) || name.length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍名字不符合规范");
        }

        String description = team.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述超出限制");
        }

        //出现不传值的情况
        Integer status = Optional.ofNullable(team.getStatus()).orElse(0);

        //出现传入不在对应值范围内的情况
        TeamStatusEnum enumByValue = TeamStatusEnum.getEnumByValue(status);

        if (enumByValue == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不符合要求");
        }

        //在全权限十私密的情况下，必须需要密码
        //if (team.getStatus() == null) {
        //    team.setStatus(0);      //公开
        //    if (password != null && password.length() > 32) {
        //        return -1;
        //    }
        //} else {
        //
        //    if (password == null || password.length() > 32) {
        //        return -1;
        //    }
        //}
        //更改为下面的

        String password = team.getPassword();
        if (TeamStatusEnum.SECRET.equals(enumByValue) && (StringUtils.isBlank(password) || password.length() > 32)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码没有设置或密码长度大于32");
        }


        Date expireTime = team.getExpireTime();
        Date now = new Date();

        if (expireTime.before(now)) {
            return -1;
        }


        Long count = this.lambdaQuery().eq(Team::getUserId, logingUser.getId()).count();

        if (count >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍数超过最大值");
        }


        //插入

        team.setUserId(logingUser.getId());
        boolean result = this.save(team);
        if (!result) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }

        UserTeam userTeam = new UserTeam();
        //userTeam.setId();
        userTeam.setUserId(logingUser.getId());
        userTeam.setTeamId(team.getId());
        userTeam.setJoinTime(now);
        //userTeam.setCreateTime();
        //userTeam.setUpdateTime();
        //userTeam.setIsDelete();


        result = userTeamService.save(userTeam);
        if (!result) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }

        return 1;
    }


    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery, boolean isAdmin) {

        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // 组合查询条件
        if (teamQuery != null) {
            Long id = teamQuery.getId();
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }

            List<Long> idList = teamQuery.getIdList();
            if (CollectionUtils.isNotEmpty(idList)) {
                queryWrapper.in("id", idList);
            }

            String searchText = teamQuery.getSearchText();
            if (StringUtils.isNotBlank(searchText)) {
                queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
            }

            String name = teamQuery.getName();
            if (StringUtils.isNotBlank(name)) {
                queryWrapper.like("name", name);
            }

            String description = teamQuery.getDescription();
            if (StringUtils.isNotBlank(description)) {
                queryWrapper.like("description", description);
            }

            Integer maxNum = teamQuery.getMaxNum();
            // 查询最大人数相等的
            if (maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }
            Long userId = teamQuery.getUserId();
            // 根据创建人来查询
            if (userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }
            // 根据状态来查询
            Integer status = teamQuery.getStatus();
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
            if (statusEnum == null) {
                statusEnum = TeamStatusEnum.PUBLIC;
            }

            if (!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)) {
                throw new BusinessException(ErrorCode.NO_AUTH);
            }
            queryWrapper.eq("status", statusEnum.getValue());
        }
        // 不展示已过期的队伍
        // expireTime is null or expireTime > now()
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        // 关联查询创建人的用户信息
        for (Team team : teamList) {
            Long userId = team.getUserId();
            if (userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            // 脱敏用户信息
            if (user != null) {
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                teamUserVO.setCreateUser(userVO);
            }
            teamUserVOList.add(teamUserVO);
        }
        return teamUserVOList;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequestDTO teamUpdateRequestDTO, User loginUser) {
        //判断传入参数是否为空
        if (teamUpdateRequestDTO == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //获取传入队伍id，判断id是否合格
        Long id = teamUpdateRequestDTO.getId();
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //通过id查看，要修改的队伍是否存在
        Team oldTeam = this.getById(id);
        if (oldTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }


        // 只有管理员或者队伍的创建者可以修改
        if (oldTeam.getUserId() != loginUser.getId() && loginUser.getUserRole() != 1) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }

        //获取队伍状态
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(teamUpdateRequestDTO.getStatus());
        //如果当前修改房间是私密状态，或者说 公开 -> 加密，则必须需要设置密码
        if (statusEnum.equals(TeamStatusEnum.SECRET)) {
            if (StringUtils.isBlank(teamUpdateRequestDTO.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密房间必须要设置密码");
            }
        }

        //创建team对象，然后进行更改
        Team updateTeam = new Team();
        BeanUtils.copyProperties(teamUpdateRequestDTO, updateTeam);
        return this.updateById(updateTeam);
    }

    @Override
    public boolean joinTeam(TeamJoinRequestDTO teamJoinRequestDTO, User loginUser) {
        //校验发是为空
        if (teamJoinRequestDTO == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        //获取要加入的队伍id
        Long teamId = teamJoinRequestDTO.getTeamId();
        Team team = this.getTeamById(teamId);
        Date expireTime = team.getExpireTime();
        //如果该队伍已经过期
        if (expireTime != null && expireTime.before(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已过期");
        }
        //判断该队伍的状态：私密，公开，加密
        Integer status = team.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        //如果是私密，则一律禁止加入
        if (TeamStatusEnum.PRIVATE.equals(teamStatusEnum)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "禁止加入私有队伍");
        }

        //如果是加密，则同时需要提供该队伍加入密码
        String password = teamJoinRequestDTO.getPassword();
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            if (StringUtils.isBlank(password) || !password.equals(team.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }

        // 该用户已加入的队伍数量
        long userId = loginUser.getId();
        // 只有一个线程能获取到锁
        RLock lock = redissonClient.getLock("meet:join_team");
        try {
            // 抢到锁并执行
            while (true) {
                //所有线程都要执行，所以没拿到锁的话需要一直等待（加入的用户过多，需要排队，但是不能让其同时执行）
                if (lock.tryLock(0, -1, TimeUnit.MILLISECONDS)) {
                    System.out.println("getLock: " + Thread.currentThread().getId());
                    QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("userId", userId);
                    long hasJoinNum = userTeamService.count(userTeamQueryWrapper);
                    if (hasJoinNum > 5) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多创建和加入 5 个队伍");
                    }
                    // 不能重复加入已加入的队伍
                    userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("userId", userId);
                    userTeamQueryWrapper.eq("teamId", teamId);
                    long hasUserJoinTeam = userTeamService.count(userTeamQueryWrapper);
                    if (hasUserJoinTeam > 0) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户已加入该队伍");
                    }
                    // 已加入队伍的人数
                    long teamHasJoinNum = this.couTeamUserByTeamId(teamId);
                    if (teamHasJoinNum >= team.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已满");
                    }
                    // 修改队伍信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(userId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }
        } catch (InterruptedException e) {
            log.error("doCacheRecommendUser error", e);
            return false;
        } finally {
            // 只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
    }


    @Override
    public boolean quitTeam(TeamQuitRequestDTO teamQuitRequestDTO, User loginUser) {
        if (teamQuitRequestDTO == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        Long teamId = teamQuitRequestDTO.getTeamId();
        if (teamId == null || teamId < 0) {     //有效减少缓存穿透
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        Team team = this.getById(teamId);

        if (team == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "要退出的队伍不能存在");
        }

        long userId = loginUser.getId();


        //校验要退出的队伍是否存在
        UserTeam one = userTeamService.lambdaQuery().eq(UserTeam::getTeamId, teamId).eq(UserTeam::getUserId, userId).one();


        if (one != null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "自己不存在于该队伍中");
        }


        long count = couTeamUserByTeamId(teamId);

        if (count == 1) {
            this.removeById(teamId);
        } else {
            // 队伍还剩至少两人
            // 是队长
            if (team.getUserId() == userId) {
                // 把队伍转移给最早加入的用户
                // 1. 查询已加入队伍的所有用户和加入时间
                QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                userTeamQueryWrapper.eq("teamId", teamId);
                userTeamQueryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
                if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                UserTeam nextUserTeam = userTeamList.get(1);
                Long nextTeamLeaderId = nextUserTeam.getUserId();
                // 更新当前队伍的队长

                team.setUserId(nextTeamLeaderId);
                boolean result = this.updateById(team);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队伍队长失败");
                }
            }
        }

        return userTeamService.removeById(teamId);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTeam(long id, User loginUser) {
        // 校验队伍是否存在
        Team team = getTeamById(id);
        long teamId = team.getId();
        // 校验你是不是队伍的队长
        if (team.getUserId() != loginUser.getId()) {
            throw new BusinessException(ErrorCode.NO_AUTH, "无访问权限");
        }
        // 移除所有加入队伍的关联信息
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId", teamId);
        boolean result = userTeamService.remove(userTeamQueryWrapper);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除队伍关联信息失败");
        }
        // 删除队伍
        return this.removeById(teamId);
    }

    /**
     * 根据 id 获取队伍信息
     *
     * @param teamId
     * @return
     */
    private Team getTeamById(Long teamId) {
        if (teamId == null || teamId <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
        }
        return team;
    }


    /**
     *
     * 获取某队伍当前人数
     *
     * @param teamId
     * @return
     */
    private long couTeamUserByTeamId(long teamId) {
        QueryWrapper<UserTeam> qw = new QueryWrapper<>();
        qw.eq("teamId", teamId);
        return userTeamService.count(qw);
    }
}
